﻿/*  
 * Helix Engine
 * http://helixengine.codeplex.com/
 * 
 * Copyright (c) 2010 - 2011 Joe Hegarty
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 * 
 * For details please see http://www.opensource.org/licenses/ms-pl.html 
 *  
 * All other rights reserved. 
 *
 */  


using System;
using System.Collections.Generic;

using HelixGame.World.Entities.Components;

using HelixEngine.Maths;

namespace HelixGame.World.Entities
{
    /// <remarks>
    /// Represents a game entity
    /// </remarks>
    public class Entity
    {
        List<EntityComponent> _entityComponents;
        List<EntityComponent> _componentsNeedingUpdate;
        List<EntityComponent> _updatersToRemove;
        List<EntityComponent> _updatersToAdd;
        bool _updateListLocked = false;
        string _archetype;

        /// <summary>
        /// Entity constructor
        /// </summary>
        /// <param name="archetype">The archetype of the entity to create</param>
        public Entity(string archetype)
        {
            _archetype = archetype;
            _entityComponents = new List<EntityComponent>();
            _componentsNeedingUpdate = new List<EntityComponent>();
            _updatersToRemove = new List<EntityComponent>();
            _updatersToAdd = new List<EntityComponent>();
        }

        /// <summary>
        /// Adds a component to the entity
        /// </summary>
        /// <typeparam name="T">The type of component to add</typeparam>
        /// <returns>The created component</returns>
        public T AddEC<T>() where T : EntityComponent, new()
        {
            if (!HasEC<T>())
            {
                T newComponent = new T();
                _entityComponents.Add(newComponent);
                newComponent.Initialise(this);

                return newComponent;
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="component"></param>
        /// <returns></returns>
        public T AddEC<T>(T component) where T : EntityComponent
        {
            if (!HasEC<T>())
            {
                _entityComponents.Add(component);
                component.Initialise(this);

                return component;
            }

            return null;
        }


        /// <summary>
        /// Adds a component to the entity
        /// </summary>
        /// <param name="ecName">The name of the component to add</param>
        /// <returns>The creatoed component</returns>
        public EntityComponent AddEC(string ecName)
        {

            try
            {
                EntityComponent newComponent = (EntityComponent)Activator.CreateInstance(null, "HelixGame.World.Entity.Components." + ecName);
                _entityComponents.Add(newComponent);
                newComponent.Initialise(this);
                return newComponent;
            }
            catch
            {
                return null; 
            }
        }

        /// <summary>
        /// Checks whether an entity component already exists on this entity
        /// </summary>
        /// <typeparam name="T">The type of component to check for</typeparam>
        /// <returns>true when the component exists</returns>
        public bool HasEC<T>() where T : EntityComponent
        {

            foreach (var ec in _entityComponents) if (ec is T) return true;
            return false;
        }

        /// <summary>
        /// Fetches an entity component
        /// </summary>
        /// <typeparam name="T">The type of component to fetch</typeparam>
        /// <param name="component">The component to populate</param>
        /// <returns>true when the component was successfully fetched</returns>
        public bool GetEC<T>(ref T component) where T : EntityComponent
        {
            foreach (var ec in _entityComponents)
            {
                if (ec is T)
                {
                    component = (T)ec;
                    return true;
                }
            }
            component = null;
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="time"></param>
        public void Update(EngineTime time)
        {
            _updateListLocked = true;
            foreach (var ec in _componentsNeedingUpdate)
            {
                ec.Update(time);
            }

            foreach (var ec in _updatersToRemove)
            {
                if (_componentsNeedingUpdate.Contains(ec))
                {
                    _componentsNeedingUpdate.Remove(ec);
                }
            }

            _updatersToRemove.Clear();

            foreach (var ec in _updatersToAdd)
            {
                if (!_componentsNeedingUpdate.Contains(ec))
                {
                    _componentsNeedingUpdate.Add(ec);
                }
            }

            _updatersToAdd.Clear();

            _updateListLocked = false;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Cleanup()
        {
            foreach (var ec in _entityComponents)
            {
                ec.Cleanup();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="component"></param>
        public void AddComponentNeedingUpdate(EntityComponent component)
        {
            if (_updateListLocked)
            {
                if (!_updatersToAdd.Contains(component))
                {
                    _updatersToAdd.Add(component);
                }
            }
            else
            {
                if (!_componentsNeedingUpdate.Contains(component))
                {
                    _componentsNeedingUpdate.Add(component);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="component"></param>
        public void RemoveComponentNeedingUpdate(EntityComponent component)
        {
            if (_updateListLocked)
            {
                if (!_updatersToRemove.Contains(component))
                {
                    _updatersToRemove.Add(component);
                }
            }
            else
            {
                if (_componentsNeedingUpdate.Contains(component))
                {
                    _componentsNeedingUpdate.Remove(component);
                }
            }
        }
    }
}
